package main

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"

	_ "github.com/mattn/go-sqlite3"
)

// sqlite数据库文件名
const DBFILE = "shortlink.db"

var ActionArgs map[string]interface{}

var db *sql.DB

func queryRow(object interface{}, stmt string, parameters ...interface{}) (result map[string]interface{}, err error) {
	var rows *sql.Rows
	if db, ok := object.(*sql.DB); ok {
		rows, err = db.Query(stmt, parameters...)
	} else if tx, ok := object.(*sql.Tx); ok {
		rows, err = tx.Query(stmt, parameters...)
	} else {
		err = errors.New("参数object的类型错误！")
		fmt.Println(err)
		return
	}
	if err != nil {
		fmt.Println("查询数据库失败：", err)
		fmt.Println(stmt, parameters)
		return
	}
	defer rows.Close()

	columns, _ := rows.Columns()
	valueArray := make([]interface{}, len(columns))
	pointArray := make([]interface{}, len(columns))

	for i := range valueArray {
		pointArray[i] = &valueArray[i]
	}

	if !rows.Next() {
		err = errors.New("数据表为空！")
		fmt.Println(err)
		fmt.Println(stmt, parameters)
		return
	}

	err = rows.Scan(pointArray...)
	if err != nil {
		fmt.Println("扫描数据行失败：", err)
		return
	}

	result = make(map[string]interface{})

	for i, column_name := range columns {
		result[column_name] = valueArray[i]
	}
	return
}

func execSTMT(object interface{}, stmt string, parameters ...interface{}) (result driver.Result, err error) {
	if db, ok := object.(*sql.DB); ok {
		result, err = db.Exec(stmt, parameters...)
	} else if tx, ok := object.(*sql.Tx); ok {
		result, err = tx.Exec(stmt, parameters...)
	} else {
		err = errors.New("参数object的类型错误！")
		fmt.Println(err)
		return
	}
	if err != nil {
		fmt.Println("语句执行失败：", err)
		fmt.Println(stmt, parameters)
		return
	}
	return
}

func modify_app_secret() {
	var (
		err     error
		matched bool
		stmt    string
	)

	app_id := *ActionArgs["app_id"].(*int)
	secret := *ActionArgs["secret"].(*string)

	matched, err = regexp.MatchString(`^\d{1,10}$`, strconv.Itoa(app_id))
	if err != nil || !matched || app_id == 0 {
		fmt.Println("参数 app_id 格式错误：", err)
		return
	}

	matched, err = regexp.MatchString(`^[0-9a-fA-F]{40}$`, secret)
	if err != nil || !matched {
		fmt.Println("参数 secret 格式错误：", err)
		return
	}

	tx, err := db.Begin()
	if err != nil {
		fmt.Println("事务开始失败：", err)
		return
	}

	stmt = `UPDATE app SET secret=? WHERE id=?;`
	_, err = execSTMT(tx, stmt, secret, app_id)
	if err != nil {
		tx.Rollback()
		return
	}

	err = tx.Commit()
	if err != nil {
		fmt.Println("事务提交失败：", err)
		tx.Rollback()
		return
	}

	fmt.Println("执行成功：", *ActionArgs["action"].(*string))
}

func getCurrentDirectory() (dir string, err error) {
	ex, err := os.Executable()
	if err != nil {
		return
	}
	dir = filepath.Dir(ex) + string(os.PathSeparator)
	return
}

func main() {
	AciontMap := map[string]interface{}{
		"modify_app_secret": modify_app_secret,
		/*
			"add_app":             add_app,
			"delete_app":          delete_app,
			"query_app_by_id":     query_app_by_id,
			"query_app_by_secret": query_app_by_secret,
			"encrypt_id":          encrypt_id,
			"decrypt_id":          decrypt_id,
			"modify_link_url":     modify_link_url,
			"modify_link_expires": modify_link_expires,
			"add_link":            add_link,
			"query_link":          query_link,
			"query_sn":            query_sn,
		*/
	}

	actions := make([]string, 0, len(AciontMap))
	for k := range AciontMap {
		actions = append(actions, k)
	}

	ActionArgs = make(map[string]interface{})
	ActionArgs["action"] = flag.String("action", "", "动作类型，支持："+strings.Join(actions, "、"))
	ActionArgs["app_id"] = flag.Int("app_id", 0, "应用ID")
	ActionArgs["secret"] = flag.String("secret", "", "应用授权密钥")
	flag.Parse()

	var err error
	dir, err := getCurrentDirectory()
	if err != nil {
		fmt.Println("获取命令路径失败：", err)
		return
	}
	db, err = sql.Open("sqlite3", dir+DBFILE)
	if err != nil {
		fmt.Println("数据库连接参数格式错误：", err)
		return
	}
	defer db.Close()

	action := *ActionArgs["action"].(*string)
	if _, ok := AciontMap[action]; ok {
		f := reflect.ValueOf(AciontMap[action])
		args := []reflect.Value{}
		f.Call(args)
	} else {
		fmt.Println("action not supported")
		return
	}
}
